module NestedAncestryCommon
  extend ActiveSupport::Concern

  included do
    audited :except => [:title]
    has_associated_audits
    has_ancestry :orphan_strategy => :restrict

    before_validation :set_title
    after_update :set_other_titles
    after_update :update_matchers, :if => proc { |obj| obj.saved_change_to_title? }

    # attribute used by *_names and *_name methods.  default is :name
    attr_name :title
  end

  # override title getter
  def title
    self[:title] || get_title
  end

  def parent_name
    parent.title if parent.present?
  end

  alias_method :to_label, :title

  def get_title
    return name if ancestry.empty?
    ancestors.map { |a| a.name + '/' }.join + name.to_s
  end

  alias_method :get_label, :get_title

  def to_param
    Parameterizable.parameterize("#{id}-#{title}")
  end

  module ClassMethods
    attr_reader :nested_attribute_fields
    def nested_attribute_for(*fields)
      @nested_attribute_fields = fields
      @nested_attribute_fields.each do |field|
        # Example method
        # def inherited_compute_profile_id
        #   read_attribute(:compute_profile_id) || nested_compute_profile_id
        # end
        define_method "inherited_#{field}" do
          self[field] || nested(field)
        end

        # Example method - only override method generated by assocation if there is ancestry.
        # if ancestry.present?
        #   def compute_profile
        #    ComputeProfile.find_by_id(inherited_compute_profile_id)
        #  end
        # end
        if (md = field.to_s.match(/(\w+)_id$/))
          define_method md[1] do
            if ancestry.present?
              klass = md[1].classify
              klass = "SmartProxy" if ["puppet_proxy", "puppet_ca_proxy"].include?(md[1])
              klass = 'Subnet::Ipv4' if md[1] == 'subnet'
              klass = 'Subnet::Ipv6' if md[1] == 'subnet6'
              klass.classify.constantize.find_by_id(send("inherited_#{field}"))
            else
              # () is required. Otherwise, get RuntimeError: implicit argument passing of super from method defined by define_method() is not supported. Specify all arguments explicitly.
              super()
            end
          end
        end
      end
    end
  end

  def nested(attr)
    self.class.sort_by_ancestry(ancestors.where.not(attr => nil)).last.try(attr) if ancestry.present?
  end

  private

  def set_title
    self.title = get_title if (name_changed? || ancestry_changed? || title.blank?)
  end

  def set_other_titles
    if saved_change_to_name? || saved_change_to_ancestry?
      self.class.where.not(ancestry: nil).find_each do |obj|
        if obj.path_ids.include?(id)
          obj.update(:title => obj.get_title)
        end
      end
    end
  end

  def obj_type
    self.class.to_s.downcase
  end

  def update_matchers
    lookup_values = LookupValue.where(:match => "#{obj_type}=#{title_before_last_save}")
    lookup_values.update_all(:match => "#{obj_type}=#{title}")
  end
end
